﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace RS.UI
{

    #region IArrangeSupport

    /// <summary>
    /// Implemented by the control that we can arrange.
    /// </summary>
    public interface IArrangeSupport
    {
        /// <summary>
        /// Returns the type of this control.
        /// </summary>
        ControlType ControlType
        { get; }

        /// <summary>
        /// Called when this control is arranged.
        /// </summary>
        void OnArrange();
    }

    #endregion

    #region ArrangeLogic

    /// <summary>
    /// This class implement a specific "arrange logic".
    /// </summary>
    abstract class ArrangeLogic
    {

        /// <summary>
        /// This list of controls used currently by this layout.
        /// </summary>
        public List<UserControl> Controls = new List<UserControl>();

        /// <summary>
        /// Tell if this logic is initialized with the default settings.
        /// </summary>
        public bool IsInitialized = false;

        /// <summary>
        /// Returns the default type of controls managed by this logic. 
        /// </summary>
        abstract public List<ControlType> DefaultControlTypes
        {
            get;
        }

        /// <summary>
        /// Arrange.
        /// </summary>
        public abstract bool Arrange();

        /// <summary>
        /// Set the content of the application window.
        /// </summary>
        public void SetWindowContent(FrameworkElement content)
        {
            ApplicationWindow window = (ApplicationWindow)App.Current.MainWindow;

            window.DocumentContent01.Content = content;
        }

        /// <summary>
        /// Prepare the control to be arranged.
        /// </summary>
        public void OnArrangeControl(FrameworkElement element)
        {
            if (!(element is IArrangeSupport))
                return;

            Arranger.CurrentControls.Add(element);

            ((IArrangeSupport)element).OnArrange();

            // Detach the parent too
            if (element.Parent == null)
                return;

            if (element.Parent is ContentControl)
                ((ContentControl)element.Parent).Content = null;
            else if (element.Parent is Panel)
                ((Panel)element.Parent).Children.Clear();
        }

    }

    #endregion

    #region enums ControlType

    /// <summary>
    /// Represent the kinds of controls.
    /// </summary>
    public enum ControlType
    {
        ViewPort_Front,
        ViewPort_Back,
        ViewPort_Top,
        ViewPort_Bottom,
        ViewPort_Right,
        ViewPort_Left,
        ViewPort_Perspective
    }

    #endregion

    #region ArrangeSchema

    /// <summary>
    /// Define the different ways to arrange the controls in the window.
    /// </summary>
    public enum ArrangeSchema
    {
        Full, // The specified control use all the space
        ViewPorts_4Views // We have 4 view (by default top, front, right, perspective)
    }

    #endregion

    /// <summary>
    /// This class is responsible of the "arrangement" of the different controls in the
    /// central space of the window.
    /// </summary>
    public static class Arranger
    {

        #region Variables

        static private Dictionary<ArrangeSchema, ArrangeLogic> _logics = new Dictionary<ArrangeSchema, ArrangeLogic>();
        static public List<IArrangeSupport> Controls = new List<IArrangeSupport>();

        static private FrameworkElement _mouseOverUIElement;

        static private ArrangeSchema _schema = ArrangeSchema.ViewPorts_4Views;

        /// <summary>
        /// Contains the historic of the schemas.
        /// </summary>
        static public Stack<ArrangeSchema> SchemasHistory = new Stack<ArrangeSchema>();

        /// <summary>
        /// The list of current controls.
        /// </summary>
        static public List<FrameworkElement> CurrentControls = new List<FrameworkElement>();

        #endregion

        #region Constructor

        static Arranger()
        {
            _logics.Add(ArrangeSchema.ViewPorts_4Views, new ArrangeLogic_4Views());
            _logics.Add(ArrangeSchema.Full, new ArrangeLogic_Full());

            Controls.Add(new ViewPort3D(ControlType.ViewPort_Top));
            Controls.Add(new ViewPort3D(ControlType.ViewPort_Front));
            Controls.Add(new ViewPort3D(ControlType.ViewPort_Right));
            Controls.Add(new ViewPort3D(ControlType.ViewPort_Perspective));
        }

        #endregion

        #region Arrange

        /// <summary>
        /// Arrange the main window space with a specific schema.
        /// </summary>
        static public void Arrange(ArrangeSchema schema)
        {
            if (!_logics.ContainsKey(schema))
                return;

            ArrangeLogic logic = _logics[schema];

            //---- Set up the default controls
            if (!logic.IsInitialized)
            {
                foreach (ControlType type in logic.DefaultControlTypes)
                    logic.Controls.Add(FindControl(type));

                logic.IsInitialized = true;
            }

            CurrentControls.Clear();

            if (!logic.Arrange())
                return;

            // Update the schema information
            _schema = schema;
            if (SchemasHistory.Count == 0 || SchemasHistory.Peek() != _schema)
                SchemasHistory.Push(_schema);
        }

        #endregion

        #region DetachACopy

        static public void DetachACopy(ControlType layoutType)
        {
            UserControl control = null;
            switch (layoutType)
            {
                case ControlType.ViewPort_Top:
                    control = new ViewPort3D(ControlType.ViewPort_Top);
                    break;
                case ControlType.ViewPort_Bottom:
                    control = new ViewPort3D(ControlType.ViewPort_Bottom);
                    break;
                case ControlType.ViewPort_Front:
                    control = new ViewPort3D(ControlType.ViewPort_Front);
                    break;
                case ControlType.ViewPort_Back:
                    control = new ViewPort3D(ControlType.ViewPort_Back);
                    break;
                case ControlType.ViewPort_Right:
                    control = new ViewPort3D(ControlType.ViewPort_Right);
                    break;
                case ControlType.ViewPort_Left:
                    control = new ViewPort3D(ControlType.ViewPort_Left);
                    break;
                case ControlType.ViewPort_Perspective:
                    control = new ViewPort3D(ControlType.ViewPort_Perspective);
                    break;
            }

            Window window = new Window();
            window.Content = control;
            window.Show();
        }

        #endregion

        #region MouseOverElement

        /// <summary>
        /// Returns the FrameworkElement the mouse is over.
        /// </summary>
        static public FrameworkElement MouseOverElement
        {
            get
            {
                return _mouseOverUIElement;
            }
        }

        /// <summary>
        /// Notify the arranger that the mouse is over a FrameworkElement.
        /// </summary>
        static public void OnMouseEnterElement(FrameworkElement uiElement)
        {
            _mouseOverUIElement = uiElement;
        }

        /// <summary>
        /// Notify the arranger that the mouse is out of a FrameworkElement.
        /// </summary>
        static public void OnMouseOutElement(FrameworkElement uiElement)
        {
            if (_mouseOverUIElement != uiElement)
                return;
            _mouseOverUIElement = null;
        }

        #endregion

        #region ArrangeSchema

        /// <summary>
        /// Returns the current schema
        /// </summary>
        static public ArrangeSchema ArrangeSchema
        {
            get
            {
                return _schema;
            }
        }

        #endregion

        #region Helpers

        static private UserControl FindControl(ControlType type)
        {
            foreach (IArrangeSupport control in Controls)
                if (control.ControlType == type)
                    return (UserControl)control;

            return null;
        }

        #endregion

    }

    #region ArrangeLogic_4Views

    sealed class ArrangeLogic_4Views : ArrangeLogic
    {

        #region DefaultControlTypes

        override public List<ControlType> DefaultControlTypes
        {
            get
            {
                List<ControlType> types = new List<ControlType>();

                types.Add(ControlType.ViewPort_Top);
                types.Add(ControlType.ViewPort_Front);
                types.Add(ControlType.ViewPort_Right);
                types.Add(ControlType.ViewPort_Perspective);

                return types;
            }
        }

        #endregion

        #region Arrange

        override public bool Arrange()
        {
            Grid grid = new Grid();

            grid.RowDefinitions.Add(new RowDefinition());
            RowDefinition rd = new RowDefinition();
            rd.Height = GridLength.Auto;
            rd.MinHeight = 3;
            grid.RowDefinitions.Add(rd);
            grid.RowDefinitions.Add(new RowDefinition());

            grid.ColumnDefinitions.Add(new ColumnDefinition());
            ColumnDefinition cd = new ColumnDefinition();
            cd.Width = GridLength.Auto;
            cd.MinWidth = 3;
            grid.ColumnDefinitions.Add(cd);
            grid.ColumnDefinitions.Add(new ColumnDefinition());

            Controls[0].SetValue(Grid.RowProperty, 0);
            Controls[0].SetValue(Grid.ColumnProperty, 0);
            Controls[1].SetValue(Grid.RowProperty, 2);
            Controls[1].SetValue(Grid.ColumnProperty, 0);
            Controls[2].SetValue(Grid.RowProperty, 2);
            Controls[2].SetValue(Grid.ColumnProperty, 2);
            Controls[3].SetValue(Grid.RowProperty, 0);
            Controls[3].SetValue(Grid.ColumnProperty, 2);

            GridSplitter horizontalSplitter = new GridSplitter();
            horizontalSplitter.ResizeBehavior = GridResizeBehavior.PreviousAndNext;
            horizontalSplitter.HorizontalAlignment = HorizontalAlignment.Stretch;
            horizontalSplitter.Height = 3;
            horizontalSplitter.SetValue(Grid.RowProperty, 1);
            horizontalSplitter.SetValue(Grid.ColumnSpanProperty, 3);

            GridSplitter verticalSplitter = new GridSplitter();
            verticalSplitter.ResizeBehavior = GridResizeBehavior.PreviousAndNext;
            verticalSplitter.VerticalAlignment = VerticalAlignment.Stretch;
            verticalSplitter.Width = 3;
            verticalSplitter.SetValue(Grid.ColumnProperty, 1);
            verticalSplitter.SetValue(Grid.RowSpanProperty, 3);

            // Necessary
            OnArrangeControl(Controls[0]);
            OnArrangeControl(Controls[1]);
            OnArrangeControl(Controls[2]);
            OnArrangeControl(Controls[3]);

            grid.Children.Add(Controls[0]);
            grid.Children.Add(Controls[1]);
            grid.Children.Add(Controls[2]);
            grid.Children.Add(Controls[3]);
            grid.Children.Add(horizontalSplitter);
            grid.Children.Add(verticalSplitter);

            //---- Set the window content
            SetWindowContent(grid);

            return true;
        }

        #endregion

    }

    #endregion

    #region ArrangeLogic_Full

    sealed class ArrangeLogic_Full : ArrangeLogic
    {

        #region DefaultControlTypes

        override public List<ControlType> DefaultControlTypes
        {
            get
            {
                return new List<ControlType>(0);
            }
        }

        #endregion

        #region Arrange

        override public bool Arrange()
        {
            //---- Returns to the previous schema
            if (Arranger.ArrangeSchema == ArrangeSchema.Full)
            {
                Arranger.SchemasHistory.Pop();
                Arranger.Arrange(Arranger.SchemasHistory.Peek());
                return false;
            }

            //---- Set the window content
            if (Arranger.MouseOverElement != null)
            {
                // Insure that this control doesn't have a parent
                OnArrangeControl(Arranger.MouseOverElement);

                ((IArrangeSupport)Arranger.MouseOverElement).OnArrange();

                SetWindowContent(Arranger.MouseOverElement);
            }

            return true;
        }

        #endregion

    }

    #endregion

}
